package com.leyou.search.serivce.impl;

import com.leyou.common.dto.PageDTO;
import com.leyou.item.client.ItemClient;
import com.leyou.item.dto.SkuDTO;
import com.leyou.item.dto.SpecParamDTO;
import com.leyou.item.dto.SpuDTO;
import com.leyou.search.entity.Goods;
import com.leyou.search.repository.GoodsRepository;
import com.leyou.search.serivce.IndexService;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.*;
import java.util.stream.Collectors;

@Service
public class IndexServiceImpl implements IndexService {

    @Autowired
    private GoodsRepository repository;

    @Autowired
    private ItemClient itemClient;

    @Override
    public void createIndexes() {
        this.repository.createIndex("{\n" +
                "  \"settings\": {\n" +
                "    \"analysis\": {\n" +
                "      \"analyzer\": {\n" +
                "        \"my_pinyin\": {\n" +
                "          \"tokenizer\": \"ik_smart\",\n" +
                "          \"filter\": [\n" +
                "            \"py\"\n" +
                "          ]\n" +
                "        }\n" +
                "      },\n" +
                "      \"filter\": {\n" +
                "        \"py\": {\n" +
                "\t\t  \"type\": \"pinyin\",\n" +
                "          \"keep_full_pinyin\": true,\n" +
                "          \"keep_joined_full_pinyin\": true,\n" +
                "          \"keep_original\": true,\n" +
                "          \"limit_first_letter_length\": 16,\n" +
                "          \"remove_duplicated_term\": true\n" +
                "        }\n" +
                "      }\n" +
                "    }\n" +
                "  },\n" +
                "  \"mappings\": {\n" +
                "    \"properties\": {\n" +
                "      \"id\": {\n" +
                "        \"type\": \"keyword\"\n" +
                "      },\n" +
                "      \"suggestion\": {\n" +
                "        \"type\": \"completion\",\n" +
                "        \"analyzer\": \"my_pinyin\",\n" +
                "        \"search_analyzer\": \"ik_smart\"\n" +
                "      },\n" +
                "      \"title\":{\n" +
                "        \"type\": \"text\",\n" +
                "        \"analyzer\": \"my_pinyin\",\n" +
                "        \"search_analyzer\": \"ik_smart\"\n" +
                "      },\n" +
                "      \"image\":{\n" +
                "        \"type\": \"keyword\",\n" +
                "        \"index\": false\n" +
                "      },\n" +
                "      \"updateTime\":{\n" +
                "        \"type\": \"date\"\n" +
                "      },\n" +
                "      \"specs\":{\n" +
                "        \"type\": \"nested\",\n" +
                "        \"properties\": {\n" +
                "          \"name\":{\"type\": \"keyword\" },\n" +
                "          \"value\":{\"type\": \"keyword\" }\n" +
                "        }\n" +
                "      }\n" +
                "    }\n" +
                "  }\n" +
                "}");
    }

    /**
     * 倒入数据，数据中基本的信息还是来自于spu，查询spu，无条件
     */
    public void addIndex() {

        int page = 1;

        while (true) {

            //item-service分页查询spu，获取到SpuDTO，停止时间为最后一次查询没有结果时
            PageDTO<SpuDTO> spuDTOPageDTO = this.itemClient.pageQuery(page, 50, null, null, true, null);

            page++;

            //分页查询 的内容为空，不是为null而是其数组的长度为0
            if (0 == spuDTOPageDTO.getItems().size()) {
                break;
            }


            //把分页查询得到的spuDTO的集合转换List<Goods>
            List<Goods> goodsList =
                    spuDTOPageDTO
                            .getItems()
                            .stream()
                            .map(spuDTO -> buildGoods(spuDTO))
                            .collect(Collectors.toList());

            //批量保存goods到索引库
            repository.saveAll(goodsList);
        }
    }


    //把传入spuDTO转为为goods类型
    public Goods buildGoods(SpuDTO spuDTO) {

        Goods goods = new Goods();

        //只会拷贝，名称相同的属性以及类型相同
        BeanUtils.copyProperties(spuDTO, goods);


        // "categoryId" : null,
        goods.setCategoryId(spuDTO.getCid3());


        //TODO 根据spuId查询对应的sku，并取出其对应的属性，封装给goods

        List<SkuDTO> skuDTOS = this.itemClient.querySkuBySpuId(spuDTO.getId());

        //"image
        //http://hw.jpg,http://ry.jpg 从字符串中进行数据截取，从第一个,截取，截取前面的
        goods.setImage(StringUtils.substringBefore(skuDTOS.get(0).getImages(),","));

        //"prices" : null,

        //取出每个sku的价格，然后存入set中，并赋值于goods
        goods.setPrices(skuDTOS.stream().map(SkuDTO::getPrice).collect(Collectors.toSet()));


        // "sold" : null, 销量，要统计每个sku的信息
        goods.setSold(skuDTOS.stream().mapToLong(SkuDTO::getSold).sum());


        // "suggestion" : null,
        //自动补全的词库，分类名称，品牌名称，title

        //a/b/c===>[a,b,c]===>a b c



        List<String> suggestions = new ArrayList<>();
        suggestions.add(spuDTO.getBrandName());
        suggestions.add(spuDTO.getTitle());
        suggestions.addAll(Arrays.asList(spuDTO.getCategoryName().split("/")));

        //把分类名称，品牌名称，title标题作为，自动补全的 "词库"
        goods.setSuggestion(suggestions);




        //"updateTime" : null

        //最好用spu的creatTime，或者sku的createTime
        goods.setUpdateTime(new Date());

        // "specs" : null,

        //list中存储多个规格参数，以及其值得map映射关系，规格参数的值有各式各样，所以value为object
        List<Map<String,Object>> specMapList = new ArrayList<>();

        //根据spuId查询对应的可搜索规格参数信息
        List<SpecParamDTO> specParamDTOS = this.itemClient.querySpecsValues(spuDTO.getId(), true);


        //把查询到的可搜索规格参数集合，存入到List的map集合中
        for (SpecParamDTO specParamDTO : specParamDTOS) {
            Map<String,Object> specMap = new HashMap<>();

            specMap.put("name", specParamDTO.getName());
            specMap.put("value", chooseSegment(specParamDTO));

            //取出规格参数的名称以及值，存入map， 存入的时候，需要查验字段是否为number并且是否有sengment字段
            //specMap.put(specParamDTO.getName(),specParamDTO.getValue());

            //把map存入list集合
            specMapList.add(specMap);
        }

        goods.setSpecs(specMapList);

        return goods;
    }

    @Override
    public void saveGoodsById(Long spuId) {

        PageDTO<SpuDTO> spuDTOPageDTO = this.itemClient.pageQuery(null, null, null, null, null, spuId);

        SpuDTO spuDTO = spuDTOPageDTO.getItems().get(0);

        repository.save(buildGoods(spuDTO));

    }

    //根据spuId删除对应的索引信息
    @Override
    public void deleteGoodsById(Long spuId) {
        repository.deleteById(spuId);
    }


    //不是数值类型，不要动，没有Segments也不要动，集合也不动
    private Object chooseSegment(SpecParamDTO p) {

        Object value = p.getValue();

        if (value == null || StringUtils.isBlank(value.toString())) {
            return "其它";
        }
        if (!p.getNumeric() || StringUtils.isBlank(p.getSegments()) || value instanceof Collection) {
            return value;
        }
        //当前属性的实际值，
        double val = parseDouble(value.toString());

        String result = "其它";
        // 保存数值段
        for (String segment : p.getSegments().split(",")) {
            String[] segs = segment.split("-");
            // 获取数值范围
            double begin = parseDouble(segs[0]);
            double end = Double.MAX_VALUE;
            if (segs.length == 2) {
                end = parseDouble(segs[1]);
            }
            // 判断是否在范围内
            if (val >= begin && val < end) {
                if (segs.length == 1) {
                    result = segs[0] + p.getUnit() + "以上";
                } else if (begin == 0) {
                    result = segs[1] + p.getUnit() + "以下";
                } else {
                    result = segment + p.getUnit();
                }
                break;
            }
        }
        return result;
    }

    private double parseDouble(String str) {
        try {
            return Double.parseDouble(str);
        } catch (Exception e) {
            return 0;
        }
    }
}
